package cz.urbangaming.galgs.algorithms.convexhull;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Deque;
import java.util.List;
import android.opengl.GLES20;
import android.util.Pair;
import cz.urbangaming.galgs.utils.Point2D;
import cz.urbangaming.galgs.utils.PolarOrderComparator;
import cz.urbangaming.galgs.utils.Utils;
import cz.urbangaming.galgs.utils.YXOrderComparator;
/**
* Convex hull with Graham's scan
* Source: Graphics Gems V., Alan W. Paeth, 1995
*
* @author Michal Karm Babacek
* @license GNU GPL 3.0
*
* @param vertices
* @return
*/
public class GrahamScan {
private static GrahamScan instance = null;
private GrahamScan() {
}
/**
* We ain't wanna more instances lying around...
*
* @return
*/
public static synchronized GrahamScan getInstance() {
if (instance == null) {
instance = new GrahamScan();
}
return instance;
}
public Pair<List<Point2D>, Integer> convexHullGrahamScan(List<Point2D> vertices) {
Deque<Point2D> verticesOnHull = new ArrayDeque<Point2D>();
Collections.sort(vertices, new YXOrderComparator());
Collections.sort(vertices, new PolarOrderComparator(vertices.get(0)));
verticesOnHull.push(vertices.get(0));
// find index firstPointNotEqual of first point not equal to points[0]
int firstPointNotEqual;
for (firstPointNotEqual = 1; firstPointNotEqual < vertices.size(); firstPointNotEqual++) {
if (!vertices.get(0).equals(vertices.get(firstPointNotEqual))) {
break;
}
}
if (firstPointNotEqual == vertices.size()) {
return null; // all points are equal
}
// find index k2 of first point not collinear with points[0] and points[firstPointNotEqual]
int k2;
for (k2 = firstPointNotEqual + 1; k2 < vertices.size(); k2++) {
if (Utils.ccw(vertices.get(0), vertices.get(firstPointNotEqual), vertices.get(k2)) != 0) {
break;
}
}
verticesOnHull.push(vertices.get(k2 - 1));
// Graham scan; note that points[N-1] is extreme point different from points[0]
for (int i = k2; i < vertices.size(); i++) {
Point2D top = verticesOnHull.pop();
while (Utils.ccw(verticesOnHull.peek(), top, vertices.get(i)) <= 0) {
top = verticesOnHull.pop();
}
verticesOnHull.push(top);
verticesOnHull.push(vertices.get(i));
}
return new Pair<List<Point2D>, Integer>(new ArrayList<Point2D>(verticesOnHull), GLES20.GL_LINE_LOOP);
}
}